package com.xuecheng.manage_cms.service.impl;

import com.alibaba.fastjson.JSON;
import com.mongodb.client.gridfs.GridFSBucket;
import com.mongodb.client.gridfs.GridFSDownloadStream;
import com.mongodb.client.gridfs.model.GridFSFile;
import com.xuecheng.framework.domain.cms.CmsPage;
import com.xuecheng.framework.domain.cms.CmsSite;
import com.xuecheng.framework.domain.cms.CmsTemplate;
import com.xuecheng.framework.domain.cms.request.QueryPageRequest;
import com.xuecheng.framework.domain.cms.response.CmsCode;
import com.xuecheng.framework.domain.cms.response.CmsPageResult;
import com.xuecheng.framework.domain.cms.response.CmsPostPageResult;
import com.xuecheng.framework.exception.ExceptionCast;
import com.xuecheng.framework.model.response.CommonCode;
import com.xuecheng.framework.model.response.QueryResponseResult;
import com.xuecheng.framework.model.response.QueryResult;
import com.xuecheng.framework.model.response.ResponseResult;
import com.xuecheng.manage_cms.config.RabbitmqConfig;
import com.xuecheng.manage_cms.dao.CmsPageRepository;
import com.xuecheng.manage_cms.dao.CmsSiteRepository;
import com.xuecheng.manage_cms.dao.CmsTemplateRepository;
import com.xuecheng.manage_cms.service.CmsPageService;
import freemarker.cache.StringTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import org.apache.commons.io.IOUtils;
import org.bson.types.ObjectId;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.*;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.gridfs.GridFsResource;
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

/**
 * @author Administrator
 * @version 1.0
 * @create 2018-09-12 18:32
 **/
@Service
public class CmsPageServiceImpl implements CmsPageService {

    @Autowired
    CmsPageRepository cmsPageRepository;
    @Autowired
    RestTemplate restTemplate;
    @Autowired
    CmsTemplateRepository cmsTemplateRepository;
    @Autowired
    GridFsTemplate gridFsTemplate;
    @Autowired
    GridFSBucket gridFSBucket;
    @Autowired
    RabbitTemplate rabbitTemplate;
    @Autowired
    CmsSiteRepository cmsSiteRepository;

    /**
     * 页面查询方法
     *
     * @param page             页码，从1开始记数
     * @param size             每页记录数
     * @param queryPageRequest 查询条件
     * @return
     */
    @Override
    public QueryResponseResult findList(int page, int size, QueryPageRequest queryPageRequest) {
        //非空判断
        if (Objects.isNull(queryPageRequest)) {
            queryPageRequest = new QueryPageRequest();
        }
        //条件匹配器
        ExampleMatcher pageAliase = ExampleMatcher.matching().
                withMatcher("pageAliase", ExampleMatcher.GenericPropertyMatchers.contains());
        //设置条件值
        CmsPage cmsPage = new CmsPage();
        if (!StringUtils.isEmpty(queryPageRequest.getSiteId())) {
            //站点id
            cmsPage.setSiteId(queryPageRequest.getSiteId());
        }
        if (!StringUtils.isEmpty(queryPageRequest.getPageAliase())) {
            //页面别名
            cmsPage.setPageAliase(queryPageRequest.getPageAliase());
        }
        //创建Example对象
        Example<CmsPage> cmsPageExample = Example.of(cmsPage, pageAliase);
        //分页参数
        if (page <= 0) {
            page = 1;
        }
        //用户查询第一页,就传  1
        page = page - 1;
        if (size <= 0) {
            size = 10;
        }
        Pageable pageable = PageRequest.of(page, size);
        //查询mongdb数据库
        Page<CmsPage> all = cmsPageRepository.findAll(cmsPageExample, pageable);
        QueryResult queryResult = new QueryResult();
        queryResult.setList(all.getContent());//数据列表
        queryResult.setTotal(all.getTotalElements());//数据总记录数
        //返回查询结果
        QueryResponseResult queryResponseResult = new QueryResponseResult(CommonCode.SUCCESS, queryResult);
        return queryResponseResult;
    }

    /**
     * 新增页面
     *
     * @param cmsPage
     * @return
     */
    @Override
    public CmsPageResult add(CmsPage cmsPage) {
        //根据页面名称、站点id、页面访问路径查询
        CmsPage one = cmsPageRepository.findByPageNameAndSiteIdAndPageWebPath(cmsPage.getPageName(), cmsPage.getSiteId(), cmsPage.getPageWebPath());
        if (one != null) {
            //添加失败
            ExceptionCast.cast(CmsCode.CMS_ADDPAGE_EXISTSNAME);
            return null;
        } else {
            //未查询到则新增
            cmsPage.setPageId(null);
            CmsPage save = cmsPageRepository.save(cmsPage);
            return new CmsPageResult(CommonCode.SUCCESS, save);
        }
    }

    /**
     * 根据id查询
     *
     * @param id
     * @return
     */
    @Override
    public CmsPage findById(String id) {
        Optional<CmsPage> optionalCmsPage = cmsPageRepository.findById(id);
        return optionalCmsPage.orElse(null);
    }

    /**
     * 编辑页面
     *
     * @param id  cmsPage表主键id
     * @param cmsPage
     * @return
     */
    @Override
    public CmsPageResult edit(String id, CmsPage cmsPage) {
        CmsPage byId = this.findById(id);
        if (byId == null) {
            //数据库没有该文档
            return new CmsPageResult(CommonCode.FAIL, null);
        }
        //数据库存在该文档,则修改
        CmsPage save = cmsPageRepository.save(cmsPage);
        return new CmsPageResult(CommonCode.SUCCESS, save);
    }

    /**
     * 删除页面
     *
     * @param id
     * @return
     */
    @Override
    public ResponseResult delete(String id) {
        CmsPage cmsPage = this.findById(id);
        if (cmsPage == null) {
            return new ResponseResult(CommonCode.FAIL);
        }
        cmsPageRepository.deleteById(id);
        return new ResponseResult(CommonCode.SUCCESS);
    }

    /**
     * 获取静态页面-->主方法
     * @param pageId
     * @return  html静态页面
     * @throws IOException
     * @throws TemplateException
     */
    public String getStaticPageHtml(String pageId) throws IOException, TemplateException {
        // 1. 获取页面模型数据
        Map dataModel = this.getModelByPageId(pageId);
        if (Objects.isNull(dataModel)) {
            //数据模型获取不到
            ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_DATAISNULL);
        }
        // 2. 获取模板文件
        String str_template = this.getTemplateByPageId(pageId);
        if (StringUtils.isEmpty(str_template)) {
            ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_TEMPLATEISNULL);
        }
        // 3. 执行静态化
        return this.generateHtml(str_template, dataModel);
    }

    /**
     * 页面静态化
     * @param str_template  字符串形式的页面模板
     * @param model  模板需要的数据模型
     * @return  静态html页面
     */
    public String generateHtml(String str_template, Map model) throws IOException, TemplateException {
        //生成配置类
        Configuration configuration = new Configuration(Configuration.getVersion());
        //模板加载器
        StringTemplateLoader stringTemplateLoader = new StringTemplateLoader();
        stringTemplateLoader.putTemplate("template", str_template);
        //配置模板加载器
        configuration.setTemplateLoader(stringTemplateLoader);
        //获取模板内容
        Template template = configuration.getTemplate("template");
        //静态化
        return FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
    }
    /**
     * 获取页面对应的模板
     *
     * @param pageId
     * @return 字符串形式的页面模板
     */
    private String getTemplateByPageId(String pageId) throws IOException {
        //取出页面信息
        CmsPage cmsPage = this.findById(pageId);
        if (cmsPage == null) {
            ExceptionCast.cast(CmsCode.CMS_PAGE_NOTEXISTS);
        }
        //获取模板
        String templateId = cmsPage.getTemplateId();
        if (StringUtils.isEmpty(templateId)) {
            ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_TEMPLATEISNULL);
        }
        //获取模板文件
        Optional<CmsTemplate> optionalCmsTemplate = cmsTemplateRepository.findById(templateId);
        if (optionalCmsTemplate.isPresent()) {
            CmsTemplate cmsTemplate = optionalCmsTemplate.get();
            //获取模板文件id
            String templateFileId = cmsTemplate.getTemplateFileId();
            //根据文件id查询文件
            GridFSFile gridFSFile = gridFsTemplate.findOne(Query.query(Criteria.where("_id").is(templateFileId)));
            //打开一个下载流对象
            GridFSDownloadStream gridFSDownloadStream = gridFSBucket.openDownloadStream(gridFSFile.getObjectId());
            //创建GridFsResource对象，获取流
            GridFsResource gridFsResource = new GridFsResource(gridFSFile, gridFSDownloadStream);
            //从流中取数据
            return IOUtils.toString(gridFsResource.getInputStream(), "utf-8");
        }
        return null;
    }

    /**
     * 获取模型数据
     */
    private Map getModelByPageId(String pageId) {
        //取出页面信息
        CmsPage cmsPage = this.findById(pageId);
        if (cmsPage == null) {
            ExceptionCast.cast(CmsCode.CMS_PAGE_NOTEXISTS);
        }
        //取出页面dataUrl
        String dataUrl = cmsPage.getDataUrl();
        if (StringUtils.isEmpty(dataUrl)) {
            ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_DATAURLISNULL);
        }
        //restTemplate请求dataUrl获取模型数据
        ResponseEntity<Map> forEntity = restTemplate.getForEntity(dataUrl, Map.class);
        return forEntity.getBody();
    }

    /**
     * 发布页面
     * @param pageId
     * @return
     */
    public ResponseResult post(String pageId) {
        // 1. 先删除, 再将静态页面存入GridFS
        CmsPage cmsPage = this.findById(pageId);
        String htmlFileId = cmsPage.getHtmlFileId();
        if (htmlFileId != null) {
            gridFsTemplate.delete(Query.query(Criteria.where("_id").is(htmlFileId)));
        }
        // 2. 执行静态化
        try {
            String staticPageHtml = this.getStaticPageHtml(pageId);
            //将静态化文件存入GridFS
            ObjectId objectId = gridFsTemplate.store(IOUtils.toInputStream(staticPageHtml, "UTF-8"), cmsPage.getPageName());
            //将html文件id更新到cmsPage中
            cmsPage.setHtmlFileId(objectId.toHexString());
            cmsPageRepository.save(cmsPage);
        } catch (IOException | TemplateException e) {
            e.printStackTrace();
            ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_SAVEHTMLERROR);
        }
        // 3. 向MQ发送消息
        Map msg = new HashMap();
        msg.put("pageId", cmsPage.getPageId());
        String json_msg = JSON.toJSONString(msg);
        //发送mq消息，route_key为页面的siteId
        rabbitTemplate.convertAndSend(RabbitmqConfig.EX_ROUTING_CMS_POSTPAGE, cmsPage.getSiteId(), json_msg);
        return new ResponseResult(CommonCode.SUCCESS);
    }

    /**
     *  向cmspage表中保存数据， 已有则更新，无则添加
     * @param cmsPage
     * @return
     */
    public CmsPageResult save(CmsPage cmsPage) {
        CmsPage one = cmsPageRepository.findByPageNameAndSiteIdAndPageWebPath(cmsPage.getPageName(), cmsPage.getSiteId(), cmsPage.getPageWebPath());
        if (one != null) {
            //进行数据更新
            cmsPage.setPageId(one.getPageId());
            CmsPage save = cmsPageRepository.save(cmsPage);
            return new CmsPageResult(CommonCode.SUCCESS, save);
        }
        //进行添加
        return this.add(cmsPage);
    }

    /**
     * 一键发布
     * @param cmsPage
     * @return
     */
    public CmsPostPageResult postPageQuick(CmsPage cmsPage) {
        // 1. 向cms_page表中保存一条数据
        CmsPageResult cmsPageResult = this.save(cmsPage);
        if (!cmsPageResult.isSuccess()) {
            ExceptionCast.cast(CommonCode.FAIL);
        }
        //获取保存后的cmsPage数据
        CmsPage cmsPageSave = cmsPageResult.getCmsPage();
        // 2. 执行页面发布（静态化、存gridFS、发送MQ）
        ResponseResult post = this.post(cmsPage.getPageId());
        if (!post.isSuccess()) {
            ExceptionCast.cast(CommonCode.FAIL);
        }
        // 3. 响应结果
        Optional<CmsSite> cmsSiteOptional = cmsSiteRepository.findById(cmsPageSave.getSiteId());
        if (!cmsSiteOptional.isPresent()) {
            ExceptionCast.cast(CommonCode.FAIL);
        }
        //获取cmsSite
        CmsSite cmsSite = cmsSiteOptional.get();
        //站点域名
        String siteDomain = cmsSite.getSiteDomain();
        //站点web路径
        String siteWebPath = cmsSite.getSiteWebPath();
        //页面路径 = 站点域名 + 站点web路径 + 页面web路径 + 页面名称
        String pathUrl = siteDomain + siteWebPath + cmsPageSave.getPageWebPath() + cmsPageSave.getPageName();
        return new CmsPostPageResult(CommonCode.SUCCESS, pathUrl);
    }
}
